<?php
/**
 * 功能：封装了应用程序的基本启动流程和初始化操作，并为应用程序提供一些公共服务。
 * 说明：
 * 创建日期：2012-04-12
 * 更新日期：
 * 作者：Mirze
 * 补充说明： 
 * -- 初始化运行环境
 * -- 提供应用程序入口
 * -- 为应用程序提供公共服务
 * -- //处理访问控制和用户信息在 session 中的存储
 *
 */
class Bootstrap
{
	/** 应用程序的基本设置 **/
    protected $_app_config;

	protected function __construct(array $app_config)
    {
		$this->_app_config = $app_config;
		$this->_initConfig(); //初始化配置文件
		M::replaceIni('app_config', $app_config); //替换

		// 设置默认的时区
        date_default_timezone_set(M::ini('default_timezone'));

		// 导入类搜索路径
        M::import($app_config['APP_DIR']);
        M::import($app_config['APP_DIR'] . '/model');
        //M::import($app_config['MODULE_DIR']);

		$app_id = isset($app_config['APP_ID']) ? trim($app_config['APP_ID']) : 'app';

        // 注册应用程序对象
        M::register($this, $app_id);
	}

	/** 引导入口 **/
	static function run(array $app_config = null)
	{
		$obj = self::instance($app_config);
		return $obj->dispatching();
	}

	/**
	 * (功能描述) 返回应用程序类的唯一实例
     *
     * @param array $app_config
     * @return MyApp
     */
    static function instance(array $app_config = null)
    {
        static $instance;
        if (is_null($instance)) {
            if (empty($app_config)) {
                die('INVALID CONSTRUCT APP');
            }
            $instance = new Bootstrap($app_config);
        }
        return $instance;
    }

	/** 页面控制器 **/
	function dispatching()
	{
		//访问控制
		$context = MContext::instance();

		// 获得请求对应的 UDI（统一目的地信息）
        $udi = $context->requestUDI('array');

		$dir = "{$this->_app_config['APP_DIR']}/controller";
        $class_name = 'controller_';
		$controller_name = $udi[MContext::UDI_CONTROLLER];
		 $class_name .= $controller_name;
		 $filename = "{$controller_name}_controller.php";

		do {
			// 载入控制器文件
			try {
				if (!class_exists($class_name, false)){
					M::loadClassFile($filename, array($dir), $class_name);
				}
			} catch (M_ClassException $ex) {
				$response = $this->_on_action_not_defined();
				break;
			} catch (M_FileException $ex) {
				$response = $this->_on_action_not_defined();
				break;
			}

			// 构造控制器对象
			$controller = new $class_name($this);
			$action_name = $udi[MContext::UDI_ACTION];
			if ($controller->existsAction($action_name)) {
				// 如果指定动作存在，则调用
				$response = $controller->execute($action_name, $args);
			} else {
				// 如果指定动作不存在，则尝试调用控制器的 _on_action_not_defined() 函数处理错误
				$response = $controller->_on_action_not_defined($action_name);
				if (is_null($response)) {
					// 如果控制器的 _on_action_not_defined() 函数没有返回处理结果
					// 则由应用程序对象的 _on_action_not_defined() 函数处理
					$response = $this->_on_action_not_defined();
				}
			}
		} while (false);
        
		if (is_object($response) && method_exists($response, 'execute'))  {
            // 如果返回结果是一个对象，并且该对象有 execute() 方法，则调用
            $response = $response->execute();
        } elseif ($response instanceof MController_Forward) {
            // 如果是一个 MController_Forward 对象，则将请求进行转发
            $response = $this->dispatching($response->args);
        }

        // 其他情况则返回执行结果
        return $response;
		
	}

	/** 初始化应用程序设置 **/
    protected function _initConfig()
    {
		// 没有使用缓存，或缓存数据失效
        $config = self::loadConfigFiles($this->_app_config);
        if ($this->_app_config['CONFIG_CACHED']) {
            //$cache->set($cache_id, $config);
        }

        M::replaceIni($config);
	}

	/**
     * 载入配置文件内容
     *
     * @param array $app_config
     *
     * @return array
     */
    static function loadConfigFiles(array $app_config)
    {
        $ext = !empty($app_config['CONFIG_FILE_EXTNAME']) ? $app_config['CONFIG_FILE_EXTNAME'] : 'config.php';
        $cfg = $app_config['CONFIG_DIR'];
        //$run_mode = strtolower($app_config['RUN_MODE']);
		$db_mode = !empty($app_config['DB_MODE']) ? strtolower($app_config['DB_MODE']) : 'read';

        $files = array(
            "{$cfg}/environment.{$ext}"               => 'global',
            "{$cfg}/database.{$ext}"                  => "db_dsn",
            "{$cfg}/acl.{$ext}"                       => 'acl_global',
            //"{$cfg}/environments/{$run_mode}.{$ext}"  => 'global',
            "{$cfg}/app.{$ext}"                       => 'appini',
            "{$cfg}/routes.{$ext}"                    => 'routes',
        );

        $replace = array();
        foreach ($app_config as $key => $value) {
            if (!is_array($value)) $replace["%{$key}%"] = $value;
        }

        $config = require(M_DIR . '/_config/default_config.php');
        foreach ($files as $filename => $scope) {
            if (!file_exists($filename)) continue;

            //$contents = Helper_YAML::load($filename, $replace);
			$contents = require($filename);
			$contents = array_change_key_case($contents);

            if ($scope == 'global') {
                $config = array_merge($config, $contents);
            } else {
                if (!isset($config[$scope]))
                {
                    $config[$scope] = array();
                }
                $config[$scope] = array_merge($config[$scope], $contents);
            }
        }

		//当前应用DB连接
        if (!empty($config['db_dsn'][$db_mode])) {
            $config['db_dsn']['default'] = $config['db_dsn'][$db_mode];
        }
        return $config;
    }

	/**
	 * (功能描述) 返回应用程序基础配置的内容
     *
     * @param string $item  如果没有提供 $item 参数，则返回所有配置的内容
     * @return mixed
     */
    function config($item = null)
    {
        if ($item) {
            return isset($this->_app_config[$item]) ? $this->_app_config[$item] : null;
        }
        return $this->_app_config;
    }




	/** 访问被拒绝时的错误处理函数 **/
	protected function _on_access_denied()
	{
		$filename = str_replace(array('/', '\\'), '/', substr(__FILE__, strlen($this->_app_config['ROOT_DIR']) + 1));
		$message = "修改文件 \"{$filename}\" 中的 _on_access_denied() 方法可以定制拒绝访问错误的处理方式";
		$error_file = $this->_app_config['APP_DIR'] . '/view/403.php';
		if(!file_exists($error_file)) {
			$error_file = M_DIR . '/core/error/403.php';
		}
		require($error_file);
	}
	/** 视图调用未定义的控制器或动作时的错误处理函数 **/
	protected function _on_action_not_defined()
	{
		$filename = str_replace(array('/', '\\'), '/', substr(__FILE__, strlen($this->_app_config['ROOT_DIR']) + 1));
		$message = "修改文件 \"{$filename}\" 中的 _on_action_not_defined() 方法可以定制页面未找到错误的处理方式";
		$error_file = $this->_app_config['APP_DIR'] . '/view/404.php';
		if(!file_exists($error_file)) {
			$error_file = M_DIR . '/core/error/404.php';
		}
		require($error_file);
	}
	/** 默认的异常处理 **/
	function exception_handler(Exception $ex)
	{
		MException::dump($ex);
		dump('如果要改变对异常的处理，请修改文件 "' . __FILE__ . '" 的 exception_handler() 方法');
	}
}